/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import Long from "./long/index"
import { int } from '../../../customTypings'

export default class TimeUnit {
    static C0: Long = Long.fromNumber(1);
    static C1: Long = TimeUnit.C0.mul(1000);
    static C2: Long = TimeUnit.C1.mul(1000);
    static C3: Long = TimeUnit.C2.mul(1000);
    static C4: Long = TimeUnit.C3.mul(60);
    static C5: Long = TimeUnit.C4.mul(60);
    static C6: Long = TimeUnit.C5.mul(24);
    static MAX: Long = Long.MAX_VALUE;

    static x(d: Long, m: Long, over: Long): Long {
        if (d > over) return Long.MAX_VALUE;
        if (d < -over) return Long.MIN_VALUE;
        return d.mul(m);
    }

    public convert(sourceDuration: Long, sourceUnit: TimeUnit): Long {
        throw new AbstractMethodError();
    }

    public toNanos(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toMicros(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toMillis(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toSeconds(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toMinutes(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toHours(duration: Long): Long {
        throw new AbstractMethodError();
    }

    public toDays(duration: Long): Long {
        throw new AbstractMethodError();
    }

    abstract excessNanos(d: Long, m: Long): int;

    public timedWait(obj: object, timeout: Long): void {
        if (timeout > 0) {
            let ms: Long = toMillis(timeout);
            let ns: int = excessNanos(timeout, ms);
            obj.wait(ms, ns);
        }
    }

    public timedJoin(thread: Thread, timeout: Long): void {
        if (timeout > 0) {
            let ms: Long = toMillis(timeout);
            let ns: int = excessNanos(timeout, ms);
            thread.join(ms, ns);
        }
    }

    public sleep(timeout: Long): void {
        if (timeout > 0) {
            let ms: Long = toMillis(timeout);
            let ns: int = excessNanos(timeout, ms);
            Thread.sleep(ms, ns);
        }
    }
}

export class NANOSECONDS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;

    public toNanos(d: Long): Long {
        return d;
    }

    public toMicros(d: Long): Long {
        return d.div(C1.div(C0));
    }

    public toMillis(d): Long {
        return d.div(C2.div(C0));
    }

    public toSeconds(d: Long): Long {
        return d.div(C3.div(C0));
    }

    public toMinutes(d: Long): Long {
        return d.div(C4.div(C0));
    }

    public toHours(d: Long): Long {
        return d.div(C5.div(C0));
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C0));
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toNanos(d);
    }

    excessNanos(d: Long, m: Long): int {
        return (d.sub(m.mul(C2))).toNumber();
    }
}


export class MICROSECONDS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C1.div(C0), MAX.div(C1.div(C0)));
    }

    public toMicros(d: Long): Long {
        return d;
    }

    public toMillis(d): Long {
        return d.div(C2.div(C1));
    }

    public toSeconds(d: Long): Long {
        return d.div(C3.div(C1));
    }

    public toMinutes(d: Long): Long {
        return d.div(C4.div(C1));
    }

    public toHours(d: Long): Long {
        return d.div(C5.div(C1));
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C1));
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toMicros(d);
    }

    excessNanos(d: Long, m: Long): int {
        return ((d.mul(C1)).sub(m.mul(C2))).toNumber();
    }
}


export class MILLISECONDS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C2.div(C0), MAX.div(C2.div(C0)));
    }

    public toMicros(d: Long): Long {
        return TimeUnit.x(d, C2.div(C1), MAX.div(C2.div(C1)));
    }

    public toMillis(d): Long {
        return d;
    }

    public toSeconds(d: Long): Long {
        return d.div(C3.div(C2));
    }

    public toMinutes(d: Long): Long {
        return d.div(C4.div(C2));
    }

    public toHours(d: Long): Long {
        return d.div(C5.div(C2));
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C2));
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toMillis(d);
    }

    excessNanos(d: Long, m: Long): int {
        return 0;
    }
}


export class SECONDS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C3.div(C0), MAX.div(C3.div(C0)));
    }

    public toMicros(d: Long): Long {
        return TimeUnit.x(d, C3.div(C1), MAX.div(C3.div(C1)));
    }

    public toMillis(d): Long {
        return TimeUnit.x(d, C3.div(C2), MAX.div(C3.div(C2)));
    }

    public toSeconds(d: Long): Long {
        return d;
    }

    public toMinutes(d: Long): Long {
        return d.div(C4.div(C3));
    }

    public toHours(d: Long): Long {
        return d.div(C5.div(C3));
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C3));
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toSeconds(d);
    }

    excessNanos(d: Long, m: Long): int {
        return 0;
    }
}


export class MINUTES {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C4.div(C0), MAX.div(C4.div(C0)));
    }

    public toMicros(d: Long): Long {
        return TimeUnit.x(d, C4.div(C1), MAX.div(C4.div(C1)));
    }

    public toMillis(d): Long {
        return TimeUnit.x(d, C4.div(C2), MAX.div(C4.div(C2)));
    }

    public toSeconds(d: Long): Long {
        return TimeUnit.x(d, C4.div(C3), MAX.div(C4.div(C3)));
    }

    public toMinutes(d: Long): Long {
        return d;
    }

    public toHours(d: Long): Long {
        return d.div(C5.div(C4));
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C4));
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toMinutes(d);
    }

    excessNanos(d: Long, m: Long): int {
        return 0;
    }
}


export class HOURS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C5.div(C0), MAX.div(C5.div(C0)));
    }

    public toMicros(d: Long): Long {
        return TimeUnit.x(d, C5.div(C1), MAX.div(C5.div(C1)));
    }

    public toMillis(d): Long  {
        return TimeUnit.x(d, C5.div(C2), MAX.div(C5.div(C2)));
    }

    public toSeconds(d: Long): Long {
        return TimeUnit.x(d, C5.div(C3), MAX.div(C5.div(C3)));
    }

    public toMinutes(d: Long): Long {
        return TimeUnit.x(d, C5.div(C4), MAX.div(C5.div(C4)));
    }

    public toHours(d: Long): Long {
        return d;
    }

    public toDays(d: Long): Long {
        return d.div(C6.div(C5))
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toHours(d);
    }

    excessNanos(d: Long, m: Long): int {
        return 0;
    }
}


export class DAYS {
    private C0: Long = TimeUnit.C0;
    private C1: Long = TimeUnit.C1;
    private C2: Long = TimeUnit.C2;
    private C3: Long = TimeUnit.C3;
    private C4: Long = TimeUnit.C4;
    private C5: Long = TimeUnit.C5;
    private C6: Long = TimeUnit.C6;
    private MAX: Long = TimeUnit.MAX;

    public toNanos(d: Long): Long {
        return TimeUnit.x(d, C6.div(C0), MAX.div(C6.div(C0)));
    }

    public toMicros(d: Long): Long {
        return TimeUnit.x(d, C6.div(C1), MAX.div(C6.div(C1)));
    }

    public toMillis(d): Long  {
        return TimeUnit.x(d, C6.div(C2), MAX.div(C6.div(C2)));
    }

    public toSeconds(d: Long): Long {
        return TimeUnit.x(d, C6.div(C3), MAX.div(C6.div(C3)));
    }

    public toMinutes(d: Long): Long {
        return TimeUnit.x(d, C6.div(C4), MAX.div(C6.div(C4)));
    }

    public toHours(d: Long): Long {
        return TimeUnit.x(d, C6.div(C5), MAX.div(C6.div(C5)));
    }

    public toDays(d: Long): Long {
        return d;
    }

    public convert(d: Long, u: TimeUnit): Long {
        return u.toDays(d);
    }

    excessNanos(d: Long, m: Long): int {
        return 0;
    }
}
